#include <numeric>

#include "OperatorCost.h"
#include "Query.h"

extern double CPU_speed;
extern double RAM_speed;
extern double HDD_access_time;
extern double NET_transmission_time;
extern double Nodes_count;

Cost::Cost(Array *array)
	: array(array)
{
}

Cost::~Cost()
{
	//delete array;
}

OperatorCost::OperatorCost(double t, double coeff, Array *array)
	: Cost(array), /*time(t), */coefficient(coeff)
{
}

OperatorCost::OperatorCost(Array *array)
	: Cost(array), /*time(0), */coefficient(1)
{
}

/*OperatorCost::OperatorCost(const OperatorCost &cost)
{
	dimensions = cost.dimensions;
	empty = cost.empty;
	time = cost.time;
	coefficient = cost.coefficient;
}*/

OperatorCost::~OperatorCost()
{
}

double OperatorCost::getCost()
{	
	//return getCount() * ((SimpleArray *)array)->empty * time * coefficient;
	return /*getCount() * */calcTime() * coefficient;
}

double OperatorCost::getCost(double count)
{
	//return count * time;
	return count * calcTime();
}

double OperatorCost::getCount()
{
	/*double count = 1;

	for (std::vector<int>::iterator i = ((SimpleArray *)array)->dimensions.begin(); i != ((SimpleArray *)array)->dimensions.end(); i++)
	{
		if (*i != 0)
		{
			count *= *i;
		}
	}

	return count;*/

	return array->getCount();
}

double OperatorCost::applyCoefficient(double count)
{
	//return coefficient * count * array->empty;
	return coefficient * count;
}

CostInfo::CostInfo(double c, double cnt)
	: cost(c), count(cnt)
{
}

void queryCost(Query *q, CostInfo *ci)
{
	for (unsigned int i = 0; i < q->lists.size(); i++)
	{
		CostInfo *c = new CostInfo(0, 0);

		queryCost(q->lists[i], c);

		ci->cost += c->cost;
		ci->count += c->count;
		ci->arr = c->arr;

		delete c;
	}

	assert(q->node->physOp);

	if (!q->lists.size())
	{
		ci->cost = q->node->physOp->cost->getCost();
		ci->count = ((OperatorCost *)q->node->physOp->cost)->getCount();
		ci->count = ((OperatorCost *)q->node->physOp->cost)->applyCoefficient(ci->count);
		ci->arr = ((OperatorCost *)q->node->physOp->cost)->getArray();
	}
	else
	{
		q->node->physOp->cost->setArray(ci->arr);
		ci->count = ((OperatorCost *)q->node->physOp->cost)->applyCoefficient(ci->count);
		ci->cost += ((OperatorCost *)q->node->physOp->cost)->getCost(ci->count);
	}
}

void Cost::setArray(Array *array)
{
	this->array = array;
}

Array *OperatorCost::getArray()
{
	return array;
}

double OperatorCost::calcTime()
{	
	assert(array);

	double t_cpu = 0;
	double t_hdd = 0;
	double t_ram = 0;
	double t_net = 0;

	ChunkArray *arr = dynamic_cast<ChunkArray *>(array);
	
	t_hdd = HDD_access_time * arr->density * arr->chunkSize * std::accumulate(arr->attributeSize.begin(), arr->attributeSize.end(), 0);
	t_cpu = arr->density * arr->chunkSize * std::accumulate(arr->attributeSize.begin(), arr->attributeSize.end(), 0) * 100000. / (CPU_speed * 10e9);
	t_ram = arr->density * arr->chunkSize * std::accumulate(arr->attributeSize.begin(), arr->attributeSize.end(), 0) * 1000. / (RAM_speed * 10e6);
	t_net = NET_transmission_time * arr->density * arr->chunkSize * std::accumulate(arr->attributeSize.begin(), arr->attributeSize.end(), 0) * 1;

	double res = (t_cpu + t_hdd + t_net) * t_ram * arr->chunksCount / Nodes_count;
	return res;
}